/*
 *  RIAnnotateFromLucenePR.java
 *
 */

package at.ofai.gate.riplugin2;


import gate.*;
import gate.creole.AbstractLanguageAnalyser;
import gate.creole.ControllerAwarePR;
import gate.creole.ExecutionException;
import gate.creole.ResourceInstantiationException;
import gate.creole.metadata.CreoleParameter;
import gate.creole.metadata.CreoleResource;
import gate.creole.metadata.Optional;
import gate.creole.metadata.RunTime;
import gate.util.GateRuntimeException;
import java.io.File;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.lucene.index.IndexReader;

/** 
 * This PR is a LA that should be run in a corpus controller. It takes a
 * Lucene Index LR as parameter and adds features for various statistics
 * to annotations (usually those annotations that were previously used
 * for creating the index). 
 */
@CreoleResource(name = "RIAnnotateFromLucenePR",
    icon = "riindex",
    //helpURL = "http://gate.ac.uk/userguide/sec:somechapter:sometopic:somepos",
    comment = "Add lucene term statistics to annotations")
public class RIAnnotateFromLucenePR
  extends AbstractLanguageAnalyser
  implements ProcessingResource, ControllerAwarePR
{

  @CreoleParameter(comment = "The annotation set to use", defaultValue="")
  @Optional
  @RunTime
  public void setAnnotationSetName(String asName) {
    this.asName = asName;
  }
  public String getAnnotationSetName() {
    return this.asName;
  }
  private String asName = "";

  @CreoleParameter(
    comment = "The annotation type name (default: Token)",
    defaultValue="Token")
  @Optional
  @RunTime
  public void setAnnotationTypeName(String typeName) {
    this.typeName = typeName;
  }
  public String getAnnotationTypeName() {
    return this.typeName;
  }
  private String typeName = "Token";


  @CreoleParameter(
    comment = "The feature name containing the term string (default: string)",
    defaultValue="string")
  @Optional
  @RunTime
  public void setFeatureName(String featureName) {
    this.featureName = featureName;
  }
  public String getFeatureName() {
    return this.featureName;
  }
  private String featureName = "string";

  @CreoleParameter(
    comment = "Context annotation type, if empty, the whole document",
    defaultValue="")
  @Optional
  @RunTime
  public void setContextAnnotationTypeName(String name) {
    contextAnnotationTypeName = name;
  }
  public String getContextAnnotationTypeName() {
    return contextAnnotationTypeName;
  }
  private String contextAnnotationTypeName = "";

  @CreoleParameter(comment = "The Lucene Index LR to use")
  @RunTime
  public void setIndexLuceneLR(RIIndexLuceneLR index) {
    this.indexLuceneLR = index;
  }

  public RIIndexLuceneLR getIndexLuceneLR() {
    return this.indexLuceneLR;
  }
  private RIIndexLuceneLR indexLuceneLR = null;


  public static final Logger logger =
    Logger.getLogger(RIBuildIndexLucenePR.class.getName());

  private IndexReader indexReader = null;
  private File luceneIndexDir = null;

  /**
   * Initialize this resource: check parameters 
   *
   * @return
   * @throws ResourceInstantiationException
   */
  @Override
  public Resource init() throws ResourceInstantiationException {
    return this;
  }

  /**
   * Process one document after the other
   * 
   * @throws ExecutionException
   */
  @Override
  public void execute() throws ExecutionException {
    // TODO: how to handle abort requests here?
    Document doc = getDocument();
    String   docName = doc.getName();
    Corpus   corp = getCorpus();
    String   corpName = corp.getName();
    // if we have a containing annotation defined, return the sorted list of
    // these annotations and create a separate lucene document for each.
    // Otherwise just create one lucene document for the whole gate document
    if(!getContextAnnotationTypeName().equals("")) {
      //System.out.println("Processing for context annotations");
      AnnotationSet as = doc.getAnnotations(getAnnotationSetName());
      AnnotationSet cas = as.get(getContextAnnotationTypeName());
      AnnotationSet las = as.get(getAnnotationTypeName());
      List<Annotation> caslist = gate.Utils.inDocumentOrder(cas);
      for(Annotation ca : caslist) {
        AnnotationSet toprocess =
          las.getContained(
            ca.getStartNode().getOffset(),
            ca.getEndNode().getOffset());
        runLuceneAnnotatorOnSet(toprocess);
      }
    } else {
      //System.out.println("Processing for whole document");
      AnnotationSet as = doc.getAnnotations(getAnnotationSetName());
      //System.out.println("Have document annotaitons: "+as.size());
      as = as.get(getAnnotationTypeName());
      //System.out.println("Have annotations of desired type: "+as.size());
      runLuceneAnnotatorOnSet(as);
    }
  }

  private void runLuceneAnnotatorOnSet(AnnotationSet as) {
    for(Annotation ann : as) {
      FeatureMap fm = ann.getFeatures();
      // get the string that represents what should have been indexed
      String termstring = (String)fm.get(getFeatureName());
      int df = indexLuceneLR.docFreq(termstring);
      int numDocs = indexLuceneLR.numDocs();
      double idf = Math.log(numDocs/(double)(df+1))+1.0;
      fm.put("lucene_df",df);
      fm.put("lucene_idf",idf);
      fm.put("lucene_numDocs",numDocs);
      fm.put("lucene_rdf",(double)df / (double) numDocs);
    }
  }

  public void controllerExecutionAborted(gate.Controller controller,
    java.lang.Throwable throwable) {
    indexLuceneLR.close();
  }

  public void controllerExecutionFinished(gate.Controller controller) {
    indexLuceneLR.close();
  }

  public void controllerExecutionStarted(gate.Controller controller) {
    // check if the controller is a corpus controller
    //System.out.println("Running controllerExecutionStarted");
    if(!(controller instanceof CorpusController)) {
      throw new GateRuntimeException("The RIIndexDocumentsPR must be run in a corpus controller");
    }    
    luceneIndexDir = indexLuceneLR.getLuceneIndexDir();

    // the open() method will return false if no data is in the directory 
    // or throw an exception if the index could not be opened.
    if(!indexLuceneLR.open()) {
      if(indexLuceneLR.numDocs() == 0) {
        throw new GateRuntimeException("Lucene Index is empty (no documents): "+indexLuceneLR);
      }
    }
  }


  
} // class RIBuildIndexLucenePR
